package com.yunxin.download.server.infra.support.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.yunxin.download.server.infra.repository.DownloadDataSourceConfigDao;
import com.yunxin.download.server.model.entity.DownloadDataSourceConfig;
import com.yunxin.download.server.model.query.result.DataSourceInfoResult;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.List;
import java.util.Objects;

/**
 * @author zero°
 * @since 2023-06-13
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class DataSourceInitListener implements ApplicationListener<ContextRefreshedEvent> {

    private static final String BOOTSTRAP = "bootstrap";

    private static final String DEFAULT_TYPE = "druid";

    private final DownloadDataSourceConfigDao downloadDataSourceConfigDao;

    @Resource
    private DynamicRoutingDataSource dataSource;


    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 防止重复执行。
        if (event.getApplicationContext().getParent() == null || BOOTSTRAP.equals(event.getApplicationContext().getParent().getId())) {
            loadDataSourceFromSysDataSource();
        }
    }

    /**
     * 添加数据源
     * @param config config
     * @return key
     */
    public String addDataSource(DownloadDataSourceConfig config){
        String key = config.getDataSourceName() + config.getSystemCode();
        DataSource source =  dataSource.getDataSources().get(key);
        if (Objects.nonNull(source)) {
            return key;
        }
        if (DEFAULT_TYPE.equalsIgnoreCase(config.getDataSourceType())) {
            DataSourceInfoResult dataSourceInfo = convertorDataSourceInfoResult(config);
            DruidDataSource druidDataSource = createDruidDataSource(dataSourceInfo);
            dataSource.addDataSource(key, druidDataSource);
            return key;
        }
        return null;
    }

    /**
     * 根据数据源名称获取数据源
     * @param dataSourceName dataSourceName
     * @return DataSource
     */
   public DataSource getDtaSource(String dataSourceName){
        return dataSource.getDataSources().get(dataSourceName);
   }

    /**
     * 加载系统数据源到dynamicDataSource中
     */
    private void loadDataSourceFromSysDataSource() {
        List<DataSourceInfoResult> results = downloadDataSourceConfigDao.listAll();
        //将该配置增加到动态数据源中去，DynamicRoutingDataSource这个bean的name默认是dataSource,当然是用@Autowired也可以
        //这里的findAllList就是去库里查询所有配置在表中的数据源
        for (DataSourceInfoResult dataSourceInfo : results) {
            String key = dataSourceInfo.getDataSourceName() + dataSourceInfo.getSystemCode();
            try {
                //这里应该增加判断，如果是druid数据库连接池就是用DruidDataSource对象
                //如果是Hikari就是用HikariDataSource
                //这里数据库连接属性很多，我们目前只设置了四个基本要素，以后可以扩充其他
                if (DEFAULT_TYPE.equalsIgnoreCase(dataSourceInfo.getDataSourceType())) {
                    DruidDataSource druidDataSource = createDruidDataSource(dataSourceInfo);
                    dataSource.addDataSource(key, druidDataSource);
                }


                log.debug("add datasource " + key);
            } catch (Exception e) {
                log.error("在系统配置的数据源[" + key + "]启动项目时无法正确加载进去，请正确配置该数据源", e);
            }
        }
    }

    /**
     * 转换 DataSourceInfoResult
     * @param config config
     * @return DataSourceInfoResult
     */
    @NotNull
    private static DataSourceInfoResult convertorDataSourceInfoResult(DownloadDataSourceConfig config) {
        DataSourceInfoResult dataSourceInfo = new DataSourceInfoResult();
        dataSourceInfo.setDataSourceUrl(config.getDataSourceUrl());
        dataSourceInfo.setDataSourceClassName(config.getDataSourceClassName());
        dataSourceInfo.setDataSourceName(config.getDataSourceName());
        dataSourceInfo.setDataSourcePassword(config.getDataSourcePassword());
        dataSourceInfo.setDataSourceUserName(config.getDataSourceUserName());
        dataSourceInfo.setDataSourceType(config.getDataSourceType());
        dataSourceInfo.setSystemCode(config.getSystemCode());
        return dataSourceInfo;
    }

    /**
     * 创建druid数据源
     *
     * @param dataSourceInfo dataSourceInfo
     * @return DruidDataSource
     */
    @NotNull
    private DruidDataSource createDruidDataSource(DataSourceInfoResult dataSourceInfo) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceInfo.getDataSourceUrl());
        druidDataSource.setUsername(dataSourceInfo.getDataSourceUserName());
        druidDataSource.setPassword(dataSourceInfo.getDataSourcePassword());
        druidDataSource.setDriverClassName(dataSourceInfo.getDataSourceClassName());
        //失败后是否中断，默认为false，会一直打印连不上一直重试
        druidDataSource.setBreakAfterAcquireFailure(true);
        //连接错误等待时间，默认为一分钟
        druidDataSource.setTimeBetweenConnectErrorMillis(6000);
        //快速失败
        druidDataSource.setFailFast(true);
        //其他两个属性不清楚，待查询
        druidDataSource.setTestOnBorrow(true);
        druidDataSource.setTestOnReturn(true);
        return druidDataSource;
    }

}
